#include "widget.h"
#include "ui_widget.h"
#include <QIcon>
#include <QSize>
#include <QPushButton>
#include <QDebug>
#include <QMediaPlayer>
#include <QUrl>
#include <QDir>
#include <QMessageBox>
#include <QTime>
#include <QSlider>
#include <QPixmap>
#include <QFile>

#define BUFFER_SIZE 128

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget),
    m_player(new QMediaPlayer),
    m_playingState(false), // C++ 规定，初始化列表的顺序应与成员声明的顺序一致，否则可能会导致不期望的行为
    m_playMode(ORDER_PLAY),
    m_timer(new QTimer(this)) // 创建了一个 QTimer 对象，并将 this 作为其父对象
    // 将当前类对象 this 传递给 QTimer 的构造函数，这样 m_timer 会和当前对象建立父子关系
    // 这种机制由 Qt 的对象树管理父子关系，确保当父对象（如 Widget）被销毁时，子对象（如 QTimer）也会自动销毁
{
    ui->setupUi(this);

    // 初始化按钮图标
    initButtonIcon();

    // 设置音量条的范围
    ui->soundBar->setRange(0, 100);

    // 清除歌词信息（避免脏数据）
    m_lyricInfo.clear();

    // 信号和槽
    // 按钮相关
    connect(ui->prevBtn, &QPushButton::clicked, this, &Widget::handlePrevSlot);
    connect(ui->playBtn, &QPushButton::clicked, this, &Widget::handlePlaySlot);
    connect(ui->nextBtn, &QPushButton::clicked, this, &Widget::handleNextSlot);
    connect(ui->modeBtn, &QPushButton::clicked, this, &Widget::handleModeSlot);
    // 多媒体时长相关
    connect(m_player, &QMediaPlayer::durationChanged, this, &Widget::handleDurationSlot);
    connect(m_player, &QMediaPlayer::positionChanged, this, &Widget::handlePositionSlot);
    // 进度条相关
    connect(ui->processBar, &QSlider::sliderMoved, m_player, &QMediaPlayer::setPosition);
    connect(ui->soundBar, &QSlider::sliderMoved, m_player, &QMediaPlayer::setVolume);

    // 定时器
    m_timer->setInterval(300); // 设置定时器超时时间（1000就是1s）
    connect(m_timer, &QTimer::timeout, this, &Widget::handleTimeoutSlot);

    // 设置多媒体路径
    m_musicPath = "D:\\QT_test\\Player\\music\\";
    QString musicName = "告白气球.mp3";
    QString musicPath = m_musicPath + musicName;
    m_player->setMedia(QUrl::fromLocalFile(musicPath));

    // 设置音量
    m_player->setVolume(80);

    // 加载musicPath目录
    loadAppointMusicDir(m_musicPath);

    // 将第一个项（索引为 0）设为当前选中的项（重色标注当前行）
    ui->musicList->setCurrentRow(0);
}

// 格式化毫秒成mm:ss
QString Widget::formatMilliseconds(int milliseconds)
{
    // 将毫秒数转换为 QTime 对象（从 00:00:00 开始）
    QTime time = QTime(0, 0, 0).addMSecs(milliseconds);
    // 格式化为 "mm:ss"
    return time.toString("mm:ss");
}

// 加载指定歌曲的歌词
void Widget::loadAppointLyric(const QString &filepath)
{
    // 清除上一首歌的歌词
    m_lyricInfo.clear();

    QFile file(filepath);

    if (file.open(QIODevice::ReadOnly) == false)
    {
        QMessageBox::warning(this, "歌词文件", "文件不存在");
        return;
    }

    char buffer[BUFFER_SIZE] = { 0 };
    while (file.atEnd() != true) // 当文件指针已经在文件末尾时，返回 true，否则返回 false。
    {
        file.readLine(buffer, sizeof(buffer) - 1);

        QString lineData = buffer;
        QStringList listInfo = lineData.split("]");

        int time = parseTime(listInfo[0].split("[")[1]);
        QString lyric = listInfo[1];

        // 插入到哈希表
        m_lyricInfo[time] = lyric;

        // 清除脏数据
        memset(buffer, 0, sizeof(buffer));
    }

    // 关闭文件
    file.close();
}

// 将时间转换成毫秒级别
int Widget::parseTime(const QString &time)
{
    int minutes = time.split(":")[0].toInt();
    int seconds = time.split(":")[1].split(".")[0].toInt();
    int millSeconds = time.split(".")[1].toInt();

    int totalMills = minutes * 60 * 1000 + seconds * 1000 + millSeconds;

    return totalMills;
}

// 处理音乐总时长
void Widget::handleDurationSlot(qint64 duration)
{
    if (duration != 0)
    {
        // formatMilliseconds 这个函数的实现通常会将给定的毫秒数拆分为分钟和秒，并返回格式化的字符串
        // 例如，150000 毫秒将被转换为 "02:30"
        QString totalTime = formatMilliseconds(duration);
        totalTime = "/ " + totalTime;
        ui->totalTime->setText(totalTime);

        ui->processBar->setRange(0, duration); // 进度条移动的总范围
    }
}

// 处理音乐当前时长
void Widget::handlePositionSlot(qint64 position)
{
    QString currentTime = formatMilliseconds(position);
    ui->currentTime->setText(currentTime);

    ui->processBar->setValue(position); // 进度条移动到当前时长所对应的位置
}

// 处理定时器超时
void Widget::handleTimeoutSlot()
{
    int currentPos = m_player->position(); // 获取当前播放进度，以毫秒为单位
    // 遍历 m_lyricInfo，通过每个 iter 键值对来找到当前歌词对应的文本
    for (auto iter = m_lyricInfo.begin(); iter != m_lyricInfo.end(); iter++)
    {
        int prevPos = iter.key(); // 获得当前歌词时间戳
        int nextPos = (iter + 1).key(); // 获得下一个歌词的时间戳（iter + 1在循环中应确保不会超出 m_lyricInfo 的范围）
        // 检查当前播放位置 currentPos 是否在 prevPos 和 nextPos 之间
        if (prevPos < currentPos && currentPos < nextPos)
        {
            ui->currentLyric->setText(iter.value()); // 输出当前歌词
            break;
        }
    }
}

// 初始化按钮图标
void Widget::initButtonIcon()
{
    // 上一首
    QIcon prevIcon(":/img/prev.png");
    ui->prevBtn->setIcon(prevIcon);
    ui->prevBtn->setIconSize(QSize(ui->prevBtn->width(), ui->prevBtn->height()));
    // 设置样式表（设置控件外观）
    ui->prevBtn->setStyleSheet("QPushButton { background-color: rgba(255, 255, 255, 0); }"); // background-color 背景颜色

    // 播放/暂停
    QIcon playIcon(":/img/play.png");
    ui->playBtn->setIcon(playIcon);
    ui->playBtn->setIconSize(QSize(ui->playBtn->width(), ui->playBtn->height()));
    ui->playBtn->setStyleSheet("QPushButton { background-color: rgba(255, 255, 255, 0); }");

    // 下一首
    QIcon nextIcon(":/img/next.png");
    ui->nextBtn->setIcon(nextIcon);
    ui->nextBtn->setIconSize(QSize(ui->nextBtn->width(), ui->nextBtn->height()));
    ui->nextBtn->setStyleSheet("QPushButton { background-color: rgba(255, 255, 255, 0); }");

    // 模式（默认顺序播放）
    QIcon modeIcon(":/img/sequence.png");
    ui->modeBtn->setIcon(modeIcon);
    ui->modeBtn->setIconSize(QSize(ui->modeBtn->width(), ui->modeBtn->height()));
    ui->modeBtn->setStyleSheet("QPushButton { background-color: rgba(255, 255, 255, 0); }");

    // 音量喇叭
    QIcon soundIcon(":/img/sound.png");
    ui->soundBtn->setIcon(soundIcon);
    ui->soundBtn->setIconSize(QSize(ui->soundBtn->width(), ui->soundBtn->height()));
    ui->soundBtn->setStyleSheet("QPushButton { background-color: rgba(255, 255, 255, 0); }");

//    ui->processBar->setStyleSheet()
}

// 槽函数
// 上一首
void Widget::handlePrevSlot()
{
    qDebug() << "上一首";

    int currentRow = ui->musicList->currentRow();
    int prevRow = 0;
    if (m_playMode == ORDER_PLAY)
    {
        prevRow = (currentRow - 1 + ui->musicList->count()) % ui->musicList->count();
    }
    else if (m_playMode == RANDOM_PLAY)
    {
        do
        {
            prevRow = qrand() % ui->musicList->count();
        }while (currentRow == prevRow);
    }
    else if(m_playMode == CIRCLE_PLAY)
    {
        prevRow = currentRow;
    }

    ui->musicList->setCurrentRow((prevRow));
    // 播放指定歌曲
    playAppointMusic();
}

// 播放
void Widget::handlePlaySlot()
{
#if 0
    if (m_playingState == false)
    {
        qDebug() << "播放";
        m_playingState = true;

        QIcon suspendIcon(":/img/suspend.png");
        ui->playBtn->setIcon(suspendIcon);

        // 播放音乐
        m_player->play();
    }
    else
    {
        qDebug() << "暂停";
        m_playingState = false;

        QIcon playIcon(":/img/play.png");
        ui->playBtn->setIcon(playIcon);

        // 暂停音乐
        m_player->pause();
    }
#else
    // 如果状态等于播放状态
    // StoppendState 停止状态
    // PlayingState 播放状态
    // PausedState 暂停状态
    if (m_player->state() == QMediaPlayer::PlayingState)
    {
        QIcon playIcon(":/img/play.png");
        ui->playBtn->setIcon(playIcon);

        // 音乐暂停
        m_player->pause();

        // 定时器停止
        m_timer->stop();
    }
    else
    {
        QIcon suspendIcon(":/img/suspend.png");
        ui->playBtn->setIcon(suspendIcon);

        // 音乐播放
        m_player->play();

        // 定时器启动
        m_timer->start();
    }
#endif

}

// 下一首
void Widget::handleNextSlot()
{
    qDebug() << "下一首";

    int currentRow = ui->musicList->currentRow();
    int nextRow = 0;
    if (m_playMode == ORDER_PLAY) // 如果是顺序播放
    {
        nextRow = (currentRow + 1) % ui->musicList->count();
    }
    else if (m_playMode == RANDOM_PLAY) // 如果是随机播放
    {
        do
        {
            nextRow = qrand() % ui->musicList->count();
        } while (currentRow == nextRow);
    }
    else if(m_playMode == CIRCLE_PLAY)// 如果是循环播放
    {
        nextRow = currentRow;
    }

    // 重色标注
    ui->musicList->setCurrentRow((nextRow));

    // 播放指定歌曲
    playAppointMusic();
}

// 模式切换
void Widget::handleModeSlot()
{
    qDebug() << "模式切换";

    // 播放模式
    m_playMode = (m_playMode + 1) % MAXNUM_PLAY;

    switch (m_playMode)
    {
        case ORDER_PLAY: // 顺序
            {
                QIcon modeIcon(":/img/sequence.png");
                ui->modeBtn->setIcon(modeIcon);
                break;
            }

        case RANDOM_PLAY: // 随机
            {
                QIcon modeIcon(":/img/random.png");
                ui->modeBtn->setIcon(modeIcon);
                break;
            }

        case CIRCLE_PLAY: // 循环
            {
                QIcon modeIcon(":/img/circle.png");
                ui->modeBtn->setIcon(modeIcon);
                break;
            }
    }
}

// 加载指定音乐路径
void Widget::loadAppointMusicDir(const QString &filepath)
{
    QDir dir(filepath);

    // 判断文件夹是否存在
    if (dir.exists() == false)
    {
        // 弹窗
        QMessageBox::warning(this, "打开文件夹", "文件夹不存在");
        return;
    }

    // 打印文件信息
    QFileInfoList fileInfos = dir.entryInfoList(QDir::Files); // 获取当前目录中的所有文件（不包括子目录）
    for (const QFileInfo &info : fileInfos) // 遍历 fileInfos 容器中的每一个元素
    {
        // 判断文件名后缀
        if (info.suffix() == "mp3") // 获取文件的扩展名
        {
            // 打印文件名
//            qDebug() << info.fileName(); // fileName 包含扩展名
            ui->musicList->addItem(info.baseName()); // baseName 不包含扩展名
        }
    }
}

// 播放指定的歌曲
void Widget::playAppointMusic(const QString &filepath)
{
    // 暂不用此参数
    // Q_UNUSED(filepath); 是一个 Qt 的宏，用来显式地标记某个变量（这里是 filepath）未被使用
    // 目的是为了避免编译器产生未使用变量的警告
    // 当你在函数中声明了一个参数或变量，但在函数实现中没有使用它时，编译器通常会发出警告，提醒你 filepath 未被使用
    // 为了消除这个警告，你可以用 Q_UNUSED(filepath); 明确表示“我知道它未被使用，但这是有意的”
    Q_UNUSED(filepath);

    // 歌曲名
    QString musicName = ui->musicList->currentItem()->text();
    ui->songName->setText(musicName);
    // 专辑照片
    QString albumPath = m_musicPath + musicName + ".png";
    QPixmap albumPix(albumPath);
    ui->albumPict->setPixmap(albumPix.scaled(ui->albumPict->width(), ui->albumPict->height())); // 原比例缩放图像
    // 歌曲
    // QUrl::fromLocalFile(absoluteName) 将本地文件路径（absoluteName）转换为 QUrl 类型
    // setMedia() 用于设置播放器将要播放的媒体。setMedia 接受一个 QMediaContent 对象，通常用来封装音频或视频文件的 URL
    QString absoluteName = m_musicPath + musicName + ".mp3";
    m_player->setMedia(QUrl::fromLocalFile(absoluteName));
    // 歌词
    QString lyricPath = m_musicPath + musicName + ".lrc";
    // 加载歌词
    loadAppointLyric(lyricPath);
#if 0
    m_player->play();
#else
    handlePlaySlot();
#endif
}

Widget::~Widget()
{
    delete ui;
}
